Date: 19 November 2018
Dataset:
November 8-16, 2018; tweets mentioning Nordstrom (and variations).
Purpose:
The DA team recently attended an R conference, which featured several talks on Text Mining and Sentiment analysis, including one from the author of the tidytext package. We wanted to take what we learned and apply it with regard to Nordstrom and how the company is discussed in the Twitter-verse.
Methodology:
Using the rtweet package, we pulled the maximum number of recent records mentioning Nordstrom (and all relevant variations – #nordstrom, @nordstrom, etc.), including re-tweets.
Results:
The following is meant to be a demonstration of the various types of text analyses that can be done with text mining and R.
First off, we used the tidytext package to separate each tweet by word, eliminate non-meaningful words (AKA “stop words”), and join words to the “Bing” sentiment lexicon. Note that “Bing” is binary – words are classified as either “Positive” or “Negative.”
nstrom_tweets_clean <- nstrom_tweets_day %>%
unnest_tokens(word, stripped_text) %>%
anti_join(stop_words) %>%
dplyr::mutate(tweet_day = as.integer(tweet_day)) %>%
dplyr::filter(!tweet_day %in% c(6, 7))
#update lexicon
nstrom_sent_bing <- nstrom_tweets_clean %>%
inner_join(get_sentiments("bing")) %>%
dplyr::filter(!tweet_day %in% c(6, 7))
nstrom_sent_bing = nstrom_sent_bing %>%
dplyr::group_by(tweet_day, sentiment, word) %>%
dplyr::summarize(n = length(word)) %>%
dplyr::ungroup() %>%
dplyr::arrange(desc(n)) %>%
dplyr::mutate(tweet_day = as.integer(tweet_day))
plot_a = ggplot(nstrom_sent_bing, aes(x=reorder(sentiment, n),
y = n, ,
fill = sentiment,
text = paste("Date: November", tweet_day, ", 2018",
"<br>Sentiment:", sentiment,
"<br>Score:", n))) +
geom_bar(stat = "identity", position = "identity") +
facet_wrap(.~tweet_day) +
ylab("Word Count") +
xlab("Sentiment Counts by Day") +
scale_fill_brewer(palette = "Set2", name = "Sentiment", direction = -1) +
theme(axis.text.x = element_blank())
ggplotly(plot_a, tooltip = "text", width = 900, height = 550)
This seems clear enough. But what words are contributing to each sentiment?
x = nstrom_tweets_clean$word
nstrom_words <- x
nstrom_words <- data.frame(nstrom_words)
nstrom_words[] <- lapply(nstrom_words, as.character)
tibblez = nstrom_words %>%
dplyr::rename(word = nstrom_words) %>%
dplyr::group_by(word) %>%
dplyr::summarize(n = length(word)) %>%
dplyr::ungroup() %>%
dplyr::arrange(desc(n))
tibblefiltered = tibblez %>%
dplyr::filter(n > 1)
attach(tibblefiltered)
barsentiment <- tibblefiltered %>%
inner_join(get_sentiments("bing"), by = c("word"))
attach(barsentiment)
plot_c = barsentiment %>%
dplyr::count(sentiment, word, n=n) %>%
dplyr::ungroup() %>%
dplyr::filter(n >= 35) %>%
# filter(!word %in% ignore_words) %>%
# filter(word != "free") %>% #doesn't need to add to weight we don't know context
dplyr::mutate(n = ifelse(sentiment == "negative", -n, n)) %>%
dplyr::mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n, fill = factor(sentiment),
text = paste("Word:", word,
"<br>Mentions:", n))) +
geom_bar(stat = "identity") +
scale_fill_brewer(palette = "Set2", name = "Sentiment", direction = -1) +
ylab("Contribution to sentiment") +
xlab("Words mentioned more than 30x") +
coord_flip()
ggplotly(plot_c, tooltip = "text", width = 900, height = 750)
Most of these words seem correctly applied to their sentiment, but a few stand out as ambiguous or misclassified:
We can remove them as neutral words (for now) and return to our charts showing sentiments by day.
nstrom_sent_bing2 = nstrom_sent_bing %>%
dplyr::filter(!word %in% c("fall", "trump", "free", "rack", "black", "puma", "credit", "money", "tree", "pop", "hit"))
plot_b = ggplot(nstrom_sent_bing2, aes(x=reorder(sentiment, n), y = n, group = 1,
text = paste("Date: November", tweet_day, ", 2018",
"<br>Sentiment:", sentiment,
"<br>Score:", n))) +
geom_bar(stat = "identity", position = "identity", aes(fill = sentiment)) +
facet_wrap(.~tweet_day) +
ylab("Word Count") +
xlab("Sentiment Counts by Day") +
scale_fill_brewer(palette = "Set2", direction = -1, name = "Sentiment") +
#scale_fill_brewer(palette = "Set2", name = "Sentiment", direction = -1) +
theme(axis.text.x = element_blank())
ggplotly(plot_b, tooltip = "text", width = 900, height = 550)
We can also use the “NRC” lexicon to track additional sentiments:
nstrom_sent_nrc <- nstrom_tweets_clean %>%
inner_join(get_sentiments("nrc")) %>%
dplyr::count(word, sentiment, sort = TRUE) %>%
dplyr::ungroup() %>%
dplyr::filter(!sentiment %in% c("positive")) %>%
dplyr::filter(!word %in% c("fall", "trump", "free", "rack", "black", "puma", "credit", "money", "tree", "pop", "hit"))
nstrom_nrc_counts = nstrom_sent_nrc %>%
dplyr::group_by(sentiment) %>%
dplyr::top_n(10) %>%
dplyr::ungroup() %>%
dplyr::mutate(word = reorder(word, n))
plot_d = ggplot(nstrom_nrc_counts, aes(x=reorder(word, n), y = n, fill = sentiment,
text = paste("Word:", word,
"<br>Sentiment:", sentiment,
"<br>Mentions:", n), group = 1)) +
geom_bar(stat = "identity") +
facet_wrap(.~sentiment, scales = "free_y", ncol = 3) +
ylab("Top 10 Words per Sentiment") +
xlab("") +
scale_fill_brewer(palette = "Paired", name = "Sentiment") +
coord_flip() +
theme(axis.text.x = element_blank())
ggplotly(plot_d, tooltip = "text", width = 900, height = 550)
Everyone loves a word cloud!
nstrom_cloud = nstrom_tweets_clean %>%
anti_join(stop_words) %>%
dplyr::ungroup() %>%
dplyr::filter(!word %in% c("fall", "trump", "free", "rack", "black", "puma", "credit", "money", "tree", "pop", "hit", "nordstrom")) %>%
dplyr::count(word) %>%
dplyr::filter(!word =="shipping") %>%
with(wordcloud(word, n, max.words = 100))

Term Frequency
We can look at term frequency by day. This just shows us that every day there are a few words that are used extremely frequently and many that are used infrequently, which intuitively makes sense.
#term frequency
day_words <- nstrom_tweets_clean %>%
dplyr::count(tweet_day, word, sort = TRUE) %>%
ungroup()
total_words <- day_words %>%
dplyr::group_by(tweet_day) %>%
dplyr::summarize(total = sum(n))
day_words <- left_join(day_words, total_words)
#let’s look at the distribution of n/total for each day, the number of times a word appears in a day divided by the total number of terms (words) in that day This is exactly what term frequency is.
plot_e = ggplot(day_words, aes(n/total, fill = factor(tweet_day),
text = paste("Date: November", tweet_day, ", 2018",
"<br>Rank:", n,
"<br>Unique Daily Words:", comma(total)))) +
geom_histogram() +
xlim(NA, 0.004) +
scale_fill_brewer(palette = "Paired", name = "Day in November 2018") +
theme(legend.position = "none") +
facet_wrap(~tweet_day, ncol = 3, scales = "free_y")
ggplotly(plot_e, tooltip = "text", width = 900, height = 550)
Zipf’s Law
Illustrating the relationship between the frequency that a word is used and its end rank with Zipf’s law (which states that the frequency that a word appears is inversely proportional to its rank).
FYI: George Zipf was a 20th century American linguist.
freq_by_rank <- day_words %>%
dplyr::group_by(tweet_day) %>%
dplyr::mutate(rank = row_number(),
`term frequency` = n/total)
freq_by_rank %>%
ggplot(aes(rank, `term frequency`,
color = factor(tweet_day))) +
geom_line(size = 0.5, alpha = 0.8, show.legend = FALSE) +
scale_color_brewer(palette = "Paired") +
scale_x_log10() +
scale_y_log10() +
ylab("term frequency")

The deviations at low rank mean that people who tweet about Nordstrom use a lower percentage of the most common words than what is expected.
Here is the same graph, but with an approximation of the slope:
# Let’s see what the exponent of the power law is for the middle section of the rank range.
rank_subset <- freq_by_rank %>%
dplyr::filter(rank < 500,
rank > 10)
# lm(log10(`term frequency`) ~ log10(rank), data = rank_subset)
freq_by_rank %>%
ggplot(aes(rank, `term frequency`, color = factor(tweet_day))) +
geom_abline(intercept = -1.1641, slope = -0.8522, color = "black", linetype = 2) +
scale_color_brewer(palette = "Paired") +
geom_line(size = 0.5, alpha = 0.8, show.legend = FALSE) +
scale_x_log10() +
scale_y_log10() +
ylab("term frequency")

TF IDF
The idea of tf-idf is to find the important words – words that stand out – by decreasing the weight for commonly used words and increasing the weight for words that are not used very much in a collection. Calculating tf-idf attempts to find the words that are important (i.e., common) in a text, but not too common.
November 8 - 16, 2018:
day_words2 <- day_words %>%
bind_tf_idf(word, tweet_day, n) %>%
dplyr::arrange(desc(tf_idf))
plot_day2 =
day_words2 %>%
dplyr::arrange(desc(tf_idf)) %>%
dplyr::mutate(word = factor(word, levels = rev(unique(word)))) %>%
dplyr::group_by(tweet_day) %>%
top_n(10) %>%
ungroup %>%
ggplot(aes(word, tf_idf, fill = factor(tweet_day),
text = paste("Word:", word,
"<br>TF IDF:", number((tf_idf),
accuracy = .0001),
"<br>TF:", number((tf),
accuracy = .0001),
"<br>IDF:", number((idf),
accuracy = .0001),
"<br>Total Daily Words:", comma(total),
"<br>Daily Mentions:", n,
"<br>Date: November", tweet_day, ", 2018"
))) +
geom_col(show.legend = FALSE) +
labs(x = NULL, y = "") +
scale_fill_brewer(palette = "Paired") +
facet_wrap(~tweet_day, ncol = 3, scales = "free") +
coord_flip() +
theme(legend.position = "none") +
theme(axis.text.x = element_text(angle=15, size = 6))
ggplotly(plot_day2, tooltip = "text", width = 900, height = 550)
# layout(showlegend = FALSE, margin = list(r = 50, b = 50, l = 50))
Tokenizing with ngrams
When a word is preceded by a negating word, its meaning becomes its inverse. Let’s take a look using the “AFINN” lexicon, which scores each sentiment with different weights. Which words in the previous charts when seen in this context had an opposite meaning?
Not…
nstrom_tweets_day[] <- lapply(nstrom_tweets_day, as.character)
nstrom_bigrams <- nstrom_tweets_day %>%
unnest_tokens(bigram, stripped_text, token = "ngrams", n = 2)
bigrams_separated <- nstrom_bigrams %>%
separate(bigram, c("word1", "word2"), sep = " ")
#how often words are preceded by a word like “not”:
bigrams_separated2 <- bigrams_separated %>%
dplyr::filter(word1 == "not") %>%
dplyr::count(word1, word2, sort = TRUE)
AFINN <- get_sentiments("afinn")
#We can then examine the most frequent words that were preceded by “not” and were associated with a sentiment.
not_words <- bigrams_separated2 %>%
dplyr::filter(word1 == "not") %>%
dplyr::inner_join(AFINN, by = c(word2 = "word")) %>%
dplyr::count(word2, score, sort = TRUE) %>%
dplyr::ungroup() %>%
dplyr::rename("n" = "n")
#which words contributed the most in the “wrong” direction. To compute that, we can multiply their score by the number of times they appear (so that a word with a score of +3 occurring 10 times has as much impact as a word with a sentiment score of +1 occurring 30 times).
plot_not = not_words %>%
dplyr::mutate(contribution = n * -score) %>%
dplyr::arrange(desc(abs(contribution))) %>%
dplyr::mutate(word2 = reorder(word2, contribution)) %>%
ggplot(aes(word2, -n * score, fill = n * score > 0,
text = paste("not ", word2, "<br> Score:", -n * score))) +
scale_fill_brewer(palette = "Set2") +
geom_col(show.legend = FALSE) +
xlab("Words preceded by NOT") +
ylab("Sentiment score * number of occurrences * -1") +
coord_flip()
ggplotly(plot_not, tooltip = "text", width = 900, height = 550) %>%
layout(showlegend = FALSE, margin = list(b = 50, l = 50))
# not_words %>%
# mutate(contribution = (nn * score)) %>%
# arrange(desc(abs(contribution))) %>%
# head(20) %>%
# mutate(word2 = reorder(word2, contribution)) %>%
# ggplot(aes(word2, nn * score, fill = nn * score > 0)) +
# geom_col(show.legend = FALSE) +
# xlab("Words preceded by \"not\"") +
# ylab("Sentiment score * number of occurrences * -1") +
# coord_flip()
Note that we multiplied the sentiment score by -1 to reflect the inverse impact of the word “not.” Here is the same concept illustrated with a few more negating words:
negation_words <- c("not", "no", "never", "don't")
negated_words <- bigrams_separated %>%
dplyr::filter(word1 %in% negation_words) %>%
dplyr::inner_join(AFINN, by = c(word2 = "word")) %>%
dplyr::count(word1, word2, score, sort = TRUE) %>%
dplyr::ungroup()
plot_neg <- negated_words %>%
dplyr::mutate(contribution = n * -score) %>%
dplyr::arrange(abs(desc(contribution))) %>%
#head(30) %>%
dplyr::mutate(word2 = reorder(word2, contribution)) %>%
ggplot(aes(word2, n * -score, fill = n * score > 0,
text = paste(word1, " ", word2, "<br> Score:", -n * score))) +
scale_fill_brewer(palette = "Set2") +
theme_minimal(base_size = 9) +
geom_col(show.legend = FALSE) +
xlab("") +
ylab("Sentiment score * number of occurrences") +
facet_wrap(~word1, ncol = 2, scales = "free_y") +
coord_flip()
ggplotly(plot_neg, tooltip = "text", width = 900, height = 550) %>%
layout(showlegend = FALSE, margin(r = 20, b = 50, l = 20))
Certain adjectives and adverbs are used for emphasis – very, really, extremely, only, actually, so… etc. We can double the score of the second word score to reflect the impact from the first word.
nstrom_counts = nstrom_tweets_day %>%
unnest_tokens(word, stripped_text) %>%
dplyr::group_by(word) %>%
dplyr::summarize(n = length(word)) %>%
dplyr::arrange(desc(n)) %>%
dplyr::ungroup()
n_y = nstrom_counts %>% dplyr::filter(str_detect(word, "y$"))
emphasis_words <- c("very", "really", "extremely", "only", "actually", "so")
emphatic_words <- bigrams_separated %>%
dplyr::filter(word1 %in% emphasis_words) %>%
dplyr::inner_join(AFINN, by = c(word2 = "word")) %>%
dplyr::count(word1, word2, score, sort = TRUE) %>%
dplyr::ungroup()
plot <- emphatic_words %>%
dplyr::mutate(contribution = 2* n * score) %>%
dplyr::arrange(abs(desc(contribution))) %>%
head(50) %>%
dplyr::mutate(word2 = reorder(word2, contribution)) %>%
ggplot(aes(word2, 2 * n * score, fill = n * score > 0,
text = paste(word1, " ", word2, "<br> Score:", n * score))) +
scale_fill_brewer(palette = "Set2", direction = -1) +
theme_minimal(base_size = 9) +
geom_col(show.legend = FALSE) +
xlab("") +
ylab("Sentiment score * number of occurrences * 2") +
facet_wrap(~word1, ncol = 2, scales = "free_y") +
coord_flip()
ggplotly(plot, tooltip = "text", width = 900, height = 550) %>%
layout(showlegend = FALSE, margin = list(r = 20, b = 50, l = 80))
Network Analysis
Finally, we can visualize a network of bigrams (paired words):
bigrams_filtered <- bigrams_separated %>%
dplyr::filter(!word1 %in% stop_words$word) %>%
dplyr::filter(!word2 %in% stop_words$word)
bigram_counts <- bigrams_filtered %>%
dplyr::count(word1, word2, sort = TRUE)
library(igraph)
bigram_graph <- bigram_counts %>%
dplyr::filter(n > 50) %>%
graph_from_data_frame()
#bigram_graph
library(ggraph)
set.seed(2017)
ggraph(bigram_graph, layout = "fr") +
theme_void() +
geom_edge_link(aes(edge_alpha = 1, color = "red"), arrow = arrow(type = "closed", length=unit(.075, "inches")), show.legend = FALSE) +
geom_node_point(color = "turquoise", size = 4, alpha = .5) +
geom_node_text(aes(label = name), size = 3, vjust = 1, hjust = 1)

Thanks for reading through; this project was fascinating to work on and we look forward to applying text mining to more areas within Nordstrom.
LS0tCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KCjwhLS0gQ1NTIC0tPgoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KdGggewogICAgYmFja2dyb3VuZC1jb2xvcjogIzdiZDNmNjsKICAgIGNvbG9yOiBibGFjazsKICAgIGZvbnQtc2l6ZTogMTBwdDsKICAgIGZvbnQtZmFtaWx5OiAiTGF0byIsIHNhbnMtc2VyaWY7CiAgICB0ZXh0LWFsaWduOiBsZWZ0OwogICAgPCEtLSBtYXJnaW4tbGVmdDogYXV0bzsgLS0+CiAgICA8IS0tIG1hcmdpbi1yaWdodDogYXV0bzsgLS0+CiAgICA8IS0tIHBhZGRpbmctdG9wOiAyNXB4OyAtLT4KICB9Cgp0ZCB7ICAvKiBUYWJsZSAgKi8KICBmb250LXNpemU6IDEwcHQ7CiAgPCEtLSB0ZXh0LWFsaWduOiBjZW50ZXI7IC0tPgogIGZvbnQtZmFtaWx5OiAiTGF0byIsIHNhbnMtc2VyaWY7CiAgPCEtLSBwYWRkaW5nLXRvcDogMjVweDsgLS0+Cn0KYSB7CiAgY29sb3I6ICMwMDg4N2Q7CiAgZm9udC1zaXplOiAxMnB0OwogIGZvbnQtZmFtaWx5OiAiTGF0byIsIHNhbnMtc2VyaWY7Cn0KYm9keSB7CiAgZm9udC1zaXplOiAxMnB0OwogIGZvbnQtZmFtaWx5OiAiTGF0byIsIHNhbnMtc2VyaWY7Cn0KCmgxIHsKICBmb250LXNpemU6IDEycHQ7Cn0KaDIgewogIGZvbnQtc2l6ZTogMTJwdDsKICBmb250LXN0eWxlOiBpdGFsaWM7fQogIApoMyB7CiAgZm9udC1zaXplOiAxNHB0OwogIGZvbnQtd2VpZ2h0OiBib2xkZXI7Cn0KCi5zaWRlbmF2IHsKICBoZWlnaHQ6IDEwMCU7CiAgd2lkdGg6IDIwMHB4OwogIHBvc2l0aW9uOiBmaXhlZDsKICB6LWluZGV4OiAxOwogIHRvcDogMDsKICBsZWZ0OiAwOwogIGJhY2tncm91bmQtY29sb3I6ICNhNmNlZTMJOwogIG92ZXJmbG93LXg6IGhpZGRlbjsKICBwYWRkaW5nLXRvcDogMjBweDsKfQoKLnNpZGVuYXYgYSB7CiAgcGFkZGluZzogNnB4IDhweCA2cHggMTZweDsKICB0ZXh0LWRlY29yYXRpb246IG5vbmU7CiAgZm9udC1zaXplOiAxNnB0OwogIGZvbnQtd2VpZ2h0OiBib2xkZXI7CiAgZm9udC1mYW1pbHk6ICJMYXRvIiwgc2Fucy1zZXJpZjsKICBjb2xvcjogI2ZmZmZmZjsKICBkaXNwbGF5OiBibG9jazsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KCi5jZW50ZXIgewogIGRpc3BsYXk6IGJsb2NrOwogIG1hcmdpbi1sZWZ0OiBhdXRvOwogIG1hcmdpbi1yaWdodDogYXV0bzsKICB3aWR0aDogMTAwJTsKfQoKLnNpZGVuYXYgYTpob3ZlciB7CiAgY29sb3I6ICNmMWYxZjE7Cn0KCi5tYWluIHsKICBtYXJnaW4tbGVmdDogMjAwcHg7IC8qIFNhbWUgYXMgdGhlIHdpZHRoIG9mIHRoZSBzaWRlbmF2ICovCgp9Ci5tYWluLWNvbnRhaW5lciB7CiAgbWF4LXdpZHRoOiAxNDAwcHg7CiAgbWFyZ2luLWxlZnQ6IGF1dG87CiAgbWFyZ2luLXJpZ2h0OiBhdXRvOwogIHBhZGRpbmc6IDI1cHgKfQogIC8qcGFkZGluZzogMHB4IDVweDsgKi8KfQoKQG1lZGlhIHNjcmVlbiBhbmQgKG1heC1oZWlnaHQ6IDQ1MHB4KSB7CiAgLnNpZGVuYXYge3BhZGRpbmctdG9wOiAxNXB4O30KICAuc2lkZW5hdiBhIHtmb250LXNpemU6IDE4cHg7fQp9Cjwvc3R5bGU+Cgo8IS0tIFRJVExFIElORk8gIC0tPgoKPGRpdiBjbGFzcz0ic2lkZW5hdiI+CiAgPGltZyBzcmM9Ii9Vc2Vycy9jODl2L0Rlc2t0b3AvYW0yX2xvZ28ucG5nIiBhbHQ9IiIgd2lkdGg9MTgwcHggY2xhc3M9ImNlbnRlciIvPgogIDxhIGhyZWY9IiNhYm91dCI+VGV4dCBNaW5pbmcgKyBTZW50aW1lbnQgQW5hbHlzaXMgd2l0aCBUd2l0dGVyPC9hPgogIDxhIGhyZWY9IiN0b3AiPiA8Zm9udCBzaXplPSIyIiBjb2xvcj0gIiMxZjc4YjQiPiB0b3Agb2YgcGFnZSA8L2ZvbnQ+PC9hPgo8L2Rpdj4KCjwhLS0gQ09OVEVOVCBTVEFSVFMgSEVSRSAgLS0+Cgo8ZGl2IGNsYXNzPSJtYWluIj4KPGRpdiBjbGFzcz0iYm9keSI+CgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSkKYGBgCgpgYGB7cn0KIyBsb2FkIHBhY2thZ2VzLCBpbnN0YWxsIGlmIG5lZWRlZApwYWNrYWdlcyA9IGMoCiAgICAgICJkcGx5ciIKICAgICwgImdncGxvdDIiCiAgICAsICJmb3JtYXR0YWJsZSIKICAgICwgInBsb3RseSIKICAgICwgIlJDb2xvckJyZXdlciIKICAgICwgInNjYWxlcyIKICAgICwgInN0cmluZ3IiCiAgICAsICJ0aWR5ciIKICAgICwgIkVsbWVSIgogICAgLCAiUkpEQkMiCiAgICAsICJrYWJsZUV4dHJhIgogICAgLCAid2VzYW5kZXJzb24iCiAgICAsICJyZXNoYXBlMiIKICAgICwgInJ0d2VldCIKICAgICwgInRpZHl0ZXh0IgogICAgLCAibHVicmlkYXRlIgogICAgLCAid29yZGNsb3VkIgogICAgKQoKcGFja2FnZS5jaGVjayA8LSBsYXBwbHkocGFja2FnZXMsIEZVTiA9IGZ1bmN0aW9uKHgpIHsKICBpZiAoIXJlcXVpcmUoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcyh4LCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogICAgbGlicmFyeSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCiAgfQp9KQoKb3B0aW9ucyhzY2lwZW49IDk5OSkKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gOSwgYmFzZV9mYW1pbHkgPSAiUm9ib3RvIikpCgpgYGAKCgpgYGB7cn0KCiMgIyBzZWFyY2ggZm9yIGFsbCByZWNlbnQgdHdlZXRzIHdpdGggTm9yZHN0cm9tIGluIHRoZW0KIyAjIG5zdHJvbV90d2VldHMgPC0gc2VhcmNoX3R3ZWV0cygibm9yZHN0cm9tIE9SICNub3Jkc3Ryb20gT1IgQG5vcmRzdHJvbSBPUiBub3Jkc3Ryb20ncyIsIG4gPSAxODAwMCwgdHlwZSA9ICJyZWNlbnQiLCBsYW5nID0gImVuIiwgaW5jbHVkZV9ydHMgPSBUUlVFKQojIAojICNleHRyYWN0IHRleHQKIyAjbnN0cm9tX3R3ZWV0cyRzdHJpcHBlZF90ZXh0IDwtIGdzdWIoImh0dHAuKiIsIiIsICBuc3Ryb21fdHdlZXRzJHRleHQpCiMgI25zdHJvbV90d2VldHMkc3RyaXBwZWRfdGV4dCA8LSBnc3ViKCJodHRwcy4qIiwiIiwgbnN0cm9tX3R3ZWV0cyRzdHJpcHBlZF90ZXh0KQojIAojICMgbnN0cm9tX3R3ZWV0c19kYXkgPC0gbnN0cm9tX3R3ZWV0cyAlPiUKIyAjICAgbXV0YXRlKHR3ZWV0X2RheSA9IGx1YnJpZGF0ZTo6ZGF5KGFzLkRhdGUoY3JlYXRlZF9hdCkpKSAlPiUKIyAjICAgZHBseXI6OnNlbGVjdChzdHJpcHBlZF90ZXh0LCB0d2VldF9kYXkpICU+JQojICMgICB1bmdyb3VwKCkKIyAKIyAjcmVtb3ZlIHN0b3Agd29yZHMKIyAjIG5zdHJvbV90d2VldHNfY2xlYW4gPC0gbnN0cm9tX3R3ZWV0cyAlPiUKIyAjICAgbXV0YXRlKHR3ZWV0X2RheSA9IGx1YnJpZGF0ZTo6ZGF5KGFzLkRhdGUoY3JlYXRlZF9hdCkpKSAlPiUKIyAjICAgZHBseXI6OnNlbGVjdChzdHJpcHBlZF90ZXh0LCB0d2VldF9kYXkpICU+JQojICMgICB1bmdyb3VwKCkgJT4lCgpuc3Ryb21fdHdlZXRzX2RheSA8LSByZWFkLmNzdigiL1VzZXJzL2M4OXYvRG9jdW1lbnRzL0dpdC9jODl2X3Byb2plY3RzL3E0XzIwMTgvYWNjb3VudHMvY29kZS9uc3Ryb21fdHdlZXRzLmNzdiIpCgpuc3Ryb21fdHdlZXRzX2RheVtdIDwtIGxhcHBseShuc3Ryb21fdHdlZXRzX2RheSwgYXMuY2hhcmFjdGVyKQpgYGAKCjxoMSBpZCA9InRvcCI+QXV0aG9yOiA8Zm9udCBjb2xvcj0iI2VlOGY3MSI+PGI+SmVzc2ljYSBNYXJ4PC9iPjwvZm9udD48L2gxPgoKI0RhdGU6IF9fPGZvbnQgY29sb3I9IiNlZThmNzEiPiAxOSBOb3ZlbWJlciAyMDE4IDwvZm9udD5fXwoKIyNEYXRhc2V0OiAKTm92ZW1iZXIgOC0xNiwgMjAxODsgdHdlZXRzIG1lbnRpb25pbmcgTm9yZHN0cm9tIChhbmQgdmFyaWF0aW9ucykuCgojI0ppcmEgU3Rvcnk6IFtOT1JEQUNFLTgzOThdKGh0dHBzOi8vamlyYS5ub3Jkc3Ryb20ubmV0L2Jyb3dzZS9OT1JEQUNFLTgzOTgpIAoKIyNDb2RlOiBbUl0oaHR0cHM6Ly9naXRsYWIubm9yZHN0cm9tLmNvbS9ub3JkYWNlL2RpZ2l0YWwvYW5hbHl0aWNzL2M4OXZfcHJvamVjdHMvYmxvYi9tYXN0ZXIvcTRfMjAxOC90ZWFtX29yZ2FuaXphdGlvbi9jb2RlL3R3aXR0ZXJfdGV4dF9taW5pbmcuUm1kKSAKIyNQdXJwb3NlOiAKVGhlIERBIHRlYW0gcmVjZW50bHkgYXR0ZW5kZWQgYW4gUiBjb25mZXJlbmNlLCB3aGljaCBmZWF0dXJlZCBzZXZlcmFsIHRhbGtzIG9uIFRleHQgTWluaW5nIGFuZCBTZW50aW1lbnQgYW5hbHlzaXMsIGluY2x1ZGluZyBvbmUgZnJvbSB0aGUgYXV0aG9yIG9mIHRoZSBgdGlkeXRleHRgIHBhY2thZ2UuIFdlIHdhbnRlZCB0byB0YWtlIHdoYXQgd2UgbGVhcm5lZCBhbmQgYXBwbHkgaXQgd2l0aCByZWdhcmQgdG8gTm9yZHN0cm9tIGFuZCBob3cgdGhlIGNvbXBhbnkgaXMgZGlzY3Vzc2VkIGluIHRoZSBUd2l0dGVyLXZlcnNlLiAKCiMjTWV0aG9kb2xvZ3k6IApVc2luZyB0aGUgYHJ0d2VldGAgcGFja2FnZSwgd2UgcHVsbGVkIHRoZSBtYXhpbXVtIG51bWJlciBvZiByZWNlbnQgcmVjb3JkcyBtZW50aW9uaW5nIE5vcmRzdHJvbSAoYW5kIGFsbCByZWxldmFudCB2YXJpYXRpb25zIC0tICNub3Jkc3Ryb20sIEBub3Jkc3Ryb20sIGV0Yy4pLCBpbmNsdWRpbmcgcmUtdHdlZXRzLiAKCiMjUmVzdWx0czogClRoZSBmb2xsb3dpbmcgaXMgbWVhbnQgdG8gYmUgYSBkZW1vbnN0cmF0aW9uIG9mIHRoZSB2YXJpb3VzIHR5cGVzIG9mIHRleHQgYW5hbHlzZXMgdGhhdCBjYW4gYmUgZG9uZSB3aXRoIHRleHQgbWluaW5nIGFuZCBSLiA8L3A+CjxwPiBGaXJzdCBvZmYsIHdlIHVzZWQgdGhlIGB0aWR5dGV4dGAgcGFja2FnZSB0byBzZXBhcmF0ZSBlYWNoIHR3ZWV0IGJ5IHdvcmQsIGVsaW1pbmF0ZSBub24tbWVhbmluZ2Z1bCB3b3JkcyAoQUtBICJzdG9wIHdvcmRzIiksIGFuZCBqb2luIHdvcmRzIHRvIHRoZSAiQmluZyIgc2VudGltZW50IGxleGljb24uIE5vdGUgdGhhdCAiQmluZyIgaXMgYmluYXJ5IC0tIHdvcmRzIGFyZSBjbGFzc2lmaWVkIGFzIGVpdGhlciAiUG9zaXRpdmUiIG9yICJOZWdhdGl2ZS4iCgpgYGB7cn0KCm5zdHJvbV90d2VldHNfY2xlYW4gPC0gbnN0cm9tX3R3ZWV0c19kYXkgJT4lIAogIHVubmVzdF90b2tlbnMod29yZCwgc3RyaXBwZWRfdGV4dCkgJT4lCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpICU+JSAKICBkcGx5cjo6bXV0YXRlKHR3ZWV0X2RheSA9IGFzLmludGVnZXIodHdlZXRfZGF5KSkgJT4lIAogIGRwbHlyOjpmaWx0ZXIoIXR3ZWV0X2RheSAlaW4lIGMoNiwgNykpCgojdXBkYXRlIGxleGljb24KCm5zdHJvbV9zZW50X2JpbmcgPC0gbnN0cm9tX3R3ZWV0c19jbGVhbiAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIikpICU+JSAKICBkcGx5cjo6ZmlsdGVyKCF0d2VldF9kYXkgJWluJSBjKDYsIDcpKQoKbnN0cm9tX3NlbnRfYmluZyA9IG5zdHJvbV9zZW50X2JpbmcgJT4lIAogIGRwbHlyOjpncm91cF9ieSh0d2VldF9kYXksIHNlbnRpbWVudCwgd29yZCkgJT4lCiAgZHBseXI6OnN1bW1hcml6ZShuID0gbGVuZ3RoKHdvcmQpKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MobikpICU+JSAKICBkcGx5cjo6bXV0YXRlKHR3ZWV0X2RheSA9IGFzLmludGVnZXIodHdlZXRfZGF5KSkKCnBsb3RfYSA9IGdncGxvdChuc3Ryb21fc2VudF9iaW5nLCBhZXMoeD1yZW9yZGVyKHNlbnRpbWVudCwgbiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBuLCAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBzZW50aW1lbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlKCJEYXRlOiBOb3ZlbWJlciIsIHR3ZWV0X2RheSwgIiwgMjAxOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+U2VudGltZW50OiIsIHNlbnRpbWVudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5TY29yZToiLCBuKSkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImlkZW50aXR5IikgKyAgCiAgZmFjZXRfd3JhcCgufnR3ZWV0X2RheSkgKyAKICB5bGFiKCJXb3JkIENvdW50IikgKyAKICB4bGFiKCJTZW50aW1lbnQgQ291bnRzIGJ5IERheSIpICsgCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIiwgbmFtZSA9ICJTZW50aW1lbnQiLCBkaXJlY3Rpb24gPSAtMSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKCmdncGxvdGx5KHBsb3RfYSwgdG9vbHRpcCA9ICJ0ZXh0Iiwgd2lkdGggPSA5MDAsIGhlaWdodCA9IDU1MCkKCmBgYAoKVGhpcyBzZWVtcyBjbGVhciBlbm91Z2guIEJ1dCB3aGF0IHdvcmRzIGFyZSBjb250cmlidXRpbmcgdG8gZWFjaCBzZW50aW1lbnQ/CgpgYGB7cn0KCnggPSBuc3Ryb21fdHdlZXRzX2NsZWFuJHdvcmQKbnN0cm9tX3dvcmRzIDwtIHgKbnN0cm9tX3dvcmRzIDwtIGRhdGEuZnJhbWUobnN0cm9tX3dvcmRzKQpuc3Ryb21fd29yZHNbXSA8LSBsYXBwbHkobnN0cm9tX3dvcmRzLCBhcy5jaGFyYWN0ZXIpCgp0aWJibGV6ID0gbnN0cm9tX3dvcmRzICU+JSAKICBkcGx5cjo6cmVuYW1lKHdvcmQgPSBuc3Ryb21fd29yZHMpICU+JQogIGRwbHlyOjpncm91cF9ieSh3b3JkKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKG4gPSBsZW5ndGgod29yZCkpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhuKSkKCgp0aWJibGVmaWx0ZXJlZCA9IHRpYmJsZXogJT4lIAogIGRwbHlyOjpmaWx0ZXIobiA+IDEpCmF0dGFjaCh0aWJibGVmaWx0ZXJlZCkKYmFyc2VudGltZW50IDwtIHRpYmJsZWZpbHRlcmVkICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSwgYnkgPSBjKCJ3b3JkIikpCgphdHRhY2goYmFyc2VudGltZW50KQoKcGxvdF9jID0gYmFyc2VudGltZW50ICU+JQogICAgZHBseXI6OmNvdW50KHNlbnRpbWVudCwgd29yZCwgbj1uKSAlPiUKICAgIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKG4gPj0gMzUpICU+JQogICMgZmlsdGVyKCF3b3JkICVpbiUgaWdub3JlX3dvcmRzKSAlPiUKICAjIGZpbHRlcih3b3JkICE9ICJmcmVlIikgJT4lICNkb2Vzbid0IG5lZWQgdG8gYWRkIHRvIHdlaWdodCB3ZSBkb24ndCBrbm93IGNvbnRleHQgCiAgICBkcGx5cjo6bXV0YXRlKG4gPSBpZmVsc2Uoc2VudGltZW50ID09ICJuZWdhdGl2ZSIsIC1uLCBuKSkgJT4lCiAgICBkcGx5cjo6bXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUKICAgIGdncGxvdChhZXMod29yZCwgbiwgZmlsbCA9IGZhY3RvcihzZW50aW1lbnQpLAogICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUoIldvcmQ6Iiwgd29yZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+TWVudGlvbnM6IiwgbikpKSArCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIiwgbmFtZSA9ICJTZW50aW1lbnQiLCBkaXJlY3Rpb24gPSAtMSkgKyAKICAgIHlsYWIoIkNvbnRyaWJ1dGlvbiB0byBzZW50aW1lbnQiKSArCiAgICB4bGFiKCJXb3JkcyBtZW50aW9uZWQgbW9yZSB0aGFuIDMweCIpICsgCiAgICBjb29yZF9mbGlwKCkgCgpnZ3Bsb3RseShwbG90X2MsIHRvb2x0aXAgPSAidGV4dCIsIHdpZHRoID0gOTAwLCBoZWlnaHQgPSA3NTApCgoKYGBgCgpNb3N0IG9mIHRoZXNlIHdvcmRzIHNlZW0gY29ycmVjdGx5IGFwcGxpZWQgdG8gdGhlaXIgc2VudGltZW50LCBidXQgYSBmZXcgc3RhbmQgb3V0IGFzIGFtYmlndW91cyBvciBtaXNjbGFzc2lmaWVkOiAKPHVsPgogIDxsaT5mcmVlPC9saT4KICA8bGk+dHJ1bXA8L2xpPgogIDxsaT5mYWxsPC9saT4KPC91bD4KV2UgY2FuIHJlbW92ZSB0aGVtIGFzIG5ldXRyYWwgd29yZHMgKGZvciBub3cpIGFuZCByZXR1cm4gdG8gb3VyIGNoYXJ0cyBzaG93aW5nIHNlbnRpbWVudHMgYnkgZGF5LiAKCmBgYHtyfQpuc3Ryb21fc2VudF9iaW5nMiA9IG5zdHJvbV9zZW50X2JpbmcgJT4lIAogIGRwbHlyOjpmaWx0ZXIoIXdvcmQgJWluJSBjKCJmYWxsIiwgInRydW1wIiwgImZyZWUiLCAicmFjayIsICJibGFjayIsICJwdW1hIiwgImNyZWRpdCIsICJtb25leSIsICJ0cmVlIiwgICJwb3AiLCAiaGl0IikpCgpwbG90X2IgPSBnZ3Bsb3QobnN0cm9tX3NlbnRfYmluZzIsIGFlcyh4PXJlb3JkZXIoc2VudGltZW50LCBuKSwgeSA9IG4sIGdyb3VwID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlKCJEYXRlOiBOb3ZlbWJlciIsIHR3ZWV0X2RheSwgIiwgMjAxOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+U2VudGltZW50OiIsIHNlbnRpbWVudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5TY29yZToiLCBuKSkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImlkZW50aXR5IiwgYWVzKGZpbGwgPSBzZW50aW1lbnQpKSArCiAgZmFjZXRfd3JhcCgufnR3ZWV0X2RheSkgKyAKICB5bGFiKCJXb3JkIENvdW50IikgKyAKICB4bGFiKCJTZW50aW1lbnQgQ291bnRzIGJ5IERheSIpICsgCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIiwgZGlyZWN0aW9uID0gLTEsIG5hbWUgPSAiU2VudGltZW50IikgKyAgCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIsIG5hbWUgPSAiU2VudGltZW50IiwgZGlyZWN0aW9uID0gLTEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKCmdncGxvdGx5KHBsb3RfYiwgdG9vbHRpcCA9ICJ0ZXh0Iiwgd2lkdGggPSA5MDAsIGhlaWdodCA9IDU1MCkKCgpgYGAKCldlIGNhbiBhbHNvIHVzZSB0aGUgIk5SQyIgbGV4aWNvbiB0byB0cmFjayBhZGRpdGlvbmFsIHNlbnRpbWVudHM6IAoKYGBge3J9Cgpuc3Ryb21fc2VudF9ucmMgPC0gbnN0cm9tX3R3ZWV0c19jbGVhbiAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSkgJT4lIAogIGRwbHlyOjpjb3VudCh3b3JkLCBzZW50aW1lbnQsIHNvcnQgPSBUUlVFKSAlPiUgCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBkcGx5cjo6ZmlsdGVyKCFzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIpKSAlPiUgCiAgZHBseXI6OmZpbHRlcighd29yZCAlaW4lIGMoImZhbGwiLCAidHJ1bXAiLCAiZnJlZSIsICJyYWNrIiwgImJsYWNrIiwgInB1bWEiLCAiY3JlZGl0IiwgIm1vbmV5IiwgInRyZWUiLCAicG9wIiwgImhpdCIpKQoKbnN0cm9tX25yY19jb3VudHMgPSBuc3Ryb21fc2VudF9ucmMgJT4lIAogIGRwbHlyOjpncm91cF9ieShzZW50aW1lbnQpICU+JQogIGRwbHlyOjp0b3BfbigxMCkgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBkcGx5cjo6bXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKQoKcGxvdF9kID0gZ2dwbG90KG5zdHJvbV9ucmNfY291bnRzLCBhZXMoeD1yZW9yZGVyKHdvcmQsIG4pLCB5ID0gbiwgZmlsbCA9IHNlbnRpbWVudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlKCJXb3JkOiIsIHdvcmQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+U2VudGltZW50OiIsIHNlbnRpbWVudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5NZW50aW9uczoiLCBuKSwgZ3JvdXAgPSAxKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyAgCiAgZmFjZXRfd3JhcCgufnNlbnRpbWVudCwgc2NhbGVzID0gImZyZWVfeSIsIG5jb2wgPSAzKSArIAogIHlsYWIoIlRvcCAxMCBXb3JkcyBwZXIgU2VudGltZW50IikgKyAKICB4bGFiKCIiKSArIAogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFpcmVkIiwgbmFtZSA9ICJTZW50aW1lbnQiKSArCiAgY29vcmRfZmxpcCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCgpnZ3Bsb3RseShwbG90X2QsIHRvb2x0aXAgPSAidGV4dCIsIHdpZHRoID0gOTAwLCBoZWlnaHQgPSA1NTApCgoKYGBgCgpFdmVyeW9uZSBsb3ZlcyBhIF9fd29yZCBjbG91ZF9fIQoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiAKbnN0cm9tX2Nsb3VkID0gbnN0cm9tX3R3ZWV0c19jbGVhbiAlPiUgCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lIAogIGRwbHlyOjpmaWx0ZXIoIXdvcmQgJWluJSBjKCJmYWxsIiwgInRydW1wIiwgImZyZWUiLCAicmFjayIsICJibGFjayIsICJwdW1hIiwgImNyZWRpdCIsICJtb25leSIsICJ0cmVlIiwgInBvcCIsICJoaXQiLCAibm9yZHN0cm9tIikpICU+JQogIGRwbHlyOjpjb3VudCh3b3JkKSAlPiUKICBkcGx5cjo6ZmlsdGVyKCF3b3JkID09InNoaXBwaW5nIikgJT4lCiAgd2l0aCh3b3JkY2xvdWQod29yZCwgbiwgbWF4LndvcmRzID0gMTAwKSkKCmBgYAojI1Rlcm0gRnJlcXVlbmN5CldlIGNhbiBsb29rIGF0IHRlcm0gZnJlcXVlbmN5IGJ5IGRheS4gVGhpcyBqdXN0IHNob3dzIHVzIHRoYXQgZXZlcnkgZGF5IHRoZXJlIGFyZSBhIGZldyB3b3JkcyB0aGF0IGFyZSB1c2VkIGV4dHJlbWVseSBmcmVxdWVudGx5IGFuZCBtYW55IHRoYXQgYXJlIHVzZWQgaW5mcmVxdWVudGx5LCB3aGljaCBpbnR1aXRpdmVseSBtYWtlcyBzZW5zZS4gCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KI3Rlcm0gZnJlcXVlbmN5CgpkYXlfd29yZHMgPC0gbnN0cm9tX3R3ZWV0c19jbGVhbiAlPiUKICBkcGx5cjo6Y291bnQodHdlZXRfZGF5LCB3b3JkLCBzb3J0ID0gVFJVRSkgJT4lIAogIHVuZ3JvdXAoKQoKdG90YWxfd29yZHMgPC0gZGF5X3dvcmRzICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkodHdlZXRfZGF5KSAlPiUgCiAgZHBseXI6OnN1bW1hcml6ZSh0b3RhbCA9IHN1bShuKSkKCmRheV93b3JkcyA8LSBsZWZ0X2pvaW4oZGF5X3dvcmRzLCB0b3RhbF93b3JkcykKCiNsZXTigJlzIGxvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBuL3RvdGFsIGZvciBlYWNoIGRheSwgdGhlIG51bWJlciBvZiB0aW1lcyBhIHdvcmQgYXBwZWFycyBpbiBhIGRheSBkaXZpZGVkIGJ5IHRoZSB0b3RhbCBudW1iZXIgb2YgdGVybXMgKHdvcmRzKSBpbiB0aGF0IGRheSBUaGlzIGlzIGV4YWN0bHkgd2hhdCB0ZXJtIGZyZXF1ZW5jeSBpcy4KcGxvdF9lID0gZ2dwbG90KGRheV93b3JkcywgYWVzKG4vdG90YWwsIGZpbGwgPSBmYWN0b3IodHdlZXRfZGF5KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZSgiRGF0ZTogTm92ZW1iZXIiLCB0d2VldF9kYXksICIsIDIwMTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+UmFuazoiLCBuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+VW5pcXVlIERhaWx5IFdvcmRzOiIsIGNvbW1hKHRvdGFsKSkpKSArCiAgZ2VvbV9oaXN0b2dyYW0oKSArCiAgeGxpbShOQSwgMC4wMDQpICsgIAogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFpcmVkIiwgbmFtZSA9ICJEYXkgaW4gTm92ZW1iZXIgMjAxOCIpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArIAogIGZhY2V0X3dyYXAofnR3ZWV0X2RheSwgbmNvbCA9IDMsIHNjYWxlcyA9ICJmcmVlX3kiKQoKZ2dwbG90bHkocGxvdF9lLCB0b29sdGlwID0gInRleHQiLCB3aWR0aCA9IDkwMCwgaGVpZ2h0ID0gNTUwKQoKCmBgYAojI1ppcGYncyBMYXcKSWxsdXN0cmF0aW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgZnJlcXVlbmN5IHRoYXQgYSB3b3JkIGlzIHVzZWQgYW5kIGl0cyBlbmQgcmFuayB3aXRoIF9fWmlwZuKAmXMgbGF3X18gKHdoaWNoIHN0YXRlcyB0aGF0IHRoZSBmcmVxdWVuY3kgdGhhdCBhIHdvcmQgYXBwZWFycyBpcyBpbnZlcnNlbHkgcHJvcG9ydGlvbmFsIHRvIGl0cyByYW5rKS4KPGJyPiBfRllJOiBHZW9yZ2UgWmlwZiB3YXMgYSAyMHRoIGNlbnR1cnkgQW1lcmljYW4gbGluZ3Vpc3QuXwoKCmBgYHtyfQoKZnJlcV9ieV9yYW5rIDwtIGRheV93b3JkcyAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHR3ZWV0X2RheSkgJT4lIAogIGRwbHlyOjptdXRhdGUocmFuayA9IHJvd19udW1iZXIoKSwgCiAgICAgICAgIGB0ZXJtIGZyZXF1ZW5jeWAgPSBuL3RvdGFsKQoKZnJlcV9ieV9yYW5rICU+JSAKICBnZ3Bsb3QoYWVzKHJhbmssIGB0ZXJtIGZyZXF1ZW5jeWAsIAogICAgICAgICAgICAgY29sb3IgPSBmYWN0b3IodHdlZXRfZGF5KSkpICsgCiAgZ2VvbV9saW5lKHNpemUgPSAwLjUsIGFscGhhID0gMC44LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlBhaXJlZCIpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKCkgKyAKICB5bGFiKCJ0ZXJtIGZyZXF1ZW5jeSIpCgpgYGAKClRoZSBkZXZpYXRpb25zIGF0IGxvdyByYW5rIG1lYW4gdGhhdCBwZW9wbGUgd2hvIHR3ZWV0IGFib3V0IE5vcmRzdHJvbSB1c2UgYSBsb3dlciBwZXJjZW50YWdlIG9mIHRoZSBtb3N0IGNvbW1vbiB3b3JkcyB0aGFuIHdoYXQgaXMgZXhwZWN0ZWQuIAoKSGVyZSBpcyB0aGUgc2FtZSBncmFwaCwgYnV0IHdpdGggYW4gYXBwcm94aW1hdGlvbiBvZiB0aGUgc2xvcGU6IAoKYGBge3J9CiMgTGV04oCZcyBzZWUgd2hhdCB0aGUgZXhwb25lbnQgb2YgdGhlIHBvd2VyIGxhdyBpcyBmb3IgdGhlIG1pZGRsZSBzZWN0aW9uIG9mIHRoZSByYW5rIHJhbmdlLgpyYW5rX3N1YnNldCA8LSBmcmVxX2J5X3JhbmsgJT4lIAogIGRwbHlyOjpmaWx0ZXIocmFuayA8IDUwMCwKICAgICAgICAgcmFuayA+IDEwKQoKIyBsbShsb2cxMChgdGVybSBmcmVxdWVuY3lgKSB+IGxvZzEwKHJhbmspLCBkYXRhID0gcmFua19zdWJzZXQpCgpmcmVxX2J5X3JhbmsgJT4lIAogIGdncGxvdChhZXMocmFuaywgYHRlcm0gZnJlcXVlbmN5YCwgY29sb3IgPSBmYWN0b3IodHdlZXRfZGF5KSkpICsgCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gLTEuMTY0MSwgc2xvcGUgPSAtMC44NTIyLCBjb2xvciA9ICJibGFjayIsIGxpbmV0eXBlID0gMikgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlBhaXJlZCIpICsgCiAgZ2VvbV9saW5lKHNpemUgPSAwLjUsIGFscGhhID0gMC44LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfeV9sb2cxMCgpICsKICB5bGFiKCJ0ZXJtIGZyZXF1ZW5jeSIpCgoKYGBgCgojI1RGIElERgpUaGUgaWRlYSBvZiB0Zi1pZGYgaXMgdG8gZmluZCB0aGUgaW1wb3J0YW50IHdvcmRzIC0tIHdvcmRzIHRoYXQgc3RhbmQgb3V0IC0tIGJ5IGRlY3JlYXNpbmcgdGhlIHdlaWdodCBmb3IgY29tbW9ubHkgdXNlZCB3b3JkcyBhbmQgaW5jcmVhc2luZyB0aGUgd2VpZ2h0IGZvciB3b3JkcyB0aGF0IGFyZSBub3QgdXNlZCB2ZXJ5IG11Y2ggaW4gYSBjb2xsZWN0aW9uLiBDYWxjdWxhdGluZyB0Zi1pZGYgYXR0ZW1wdHMgdG8gZmluZCB0aGUgd29yZHMgdGhhdCBhcmUgaW1wb3J0YW50IChpLmUuLCBjb21tb24pIGluIGEgdGV4dCwgYnV0IG5vdCB0b28gY29tbW9uLiAKPHA+IF9Ob3ZlbWJlciA4IC0gMTYsIDIwMTg6XyA8L3A+CmBgYHtyfQoKZGF5X3dvcmRzMiA8LSBkYXlfd29yZHMgJT4lCiAgYmluZF90Zl9pZGYod29yZCwgdHdlZXRfZGF5LCBuKSAlPiUgCiAgZHBseXI6OmFycmFuZ2UoZGVzYyh0Zl9pZGYpKQoKcGxvdF9kYXkyID0gCmRheV93b3JkczIgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyh0Zl9pZGYpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHdvcmQgPSBmYWN0b3Iod29yZCwgbGV2ZWxzID0gcmV2KHVuaXF1ZSh3b3JkKSkpKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHR3ZWV0X2RheSkgJT4lIAogIHRvcF9uKDEwKSAlPiUgCiAgdW5ncm91cCAlPiUKICBnZ3Bsb3QoYWVzKHdvcmQsIHRmX2lkZiwgZmlsbCA9IGZhY3Rvcih0d2VldF9kYXkpLCAKICAgICAgICAgICAgIHRleHQgPSBwYXN0ZSgiV29yZDoiLCB3b3JkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPlRGIElERjoiLCBudW1iZXIoKHRmX2lkZiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNjdXJhY3kgPSAuMDAwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5URjoiLCBudW1iZXIoKHRmKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2N1cmFjeSA9IC4wMDAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPklERjoiLCBudW1iZXIoKGlkZiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNjdXJhY3kgPSAuMDAwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5Ub3RhbCBEYWlseSBXb3JkczoiLCBjb21tYSh0b3RhbCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5EYWlseSBNZW50aW9uczoiLCBuLAogICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+RGF0ZTogTm92ZW1iZXIiLCB0d2VldF9kYXksICIsIDIwMTgiCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9ICIiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJQYWlyZWQiKSArCiAgZmFjZXRfd3JhcCh+dHdlZXRfZGF5LCBuY29sID0gMywgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTE1LCBzaXplID0gNikpCgpnZ3Bsb3RseShwbG90X2RheTIsIHRvb2x0aXAgPSAidGV4dCIsIHdpZHRoID0gOTAwLCBoZWlnaHQgPSA1NTApCiMgICBsYXlvdXQoc2hvd2xlZ2VuZCA9IEZBTFNFLCBtYXJnaW4gPSBsaXN0KHIgPSA1MCwgYiA9IDUwLCBsID0gNTApKQoKYGBgCgojI1Rva2VuaXppbmcgd2l0aCBuZ3JhbXMKPGJyPldoZW4gYSB3b3JkIGlzIHByZWNlZGVkIGJ5IGEgbmVnYXRpbmcgd29yZCwgaXRzIG1lYW5pbmcgYmVjb21lcyBpdHMgaW52ZXJzZS4gTGV0J3MgdGFrZSBhIGxvb2sgdXNpbmcgdGhlICJBRklOTiIgbGV4aWNvbiwgd2hpY2ggc2NvcmVzIGVhY2ggc2VudGltZW50IHdpdGggZGlmZmVyZW50IHdlaWdodHMuIFdoaWNoIHdvcmRzIGluIHRoZSBwcmV2aW91cyBjaGFydHMgd2hlbiBzZWVuIGluIHRoaXMgY29udGV4dCBoYWQgYW4gb3Bwb3NpdGUgbWVhbmluZz8gCjxwPiBfX05vdC4uLl9fIDwvcD4KCgpgYGB7cn0KCm5zdHJvbV90d2VldHNfZGF5W10gPC0gbGFwcGx5KG5zdHJvbV90d2VldHNfZGF5LCBhcy5jaGFyYWN0ZXIpCgpuc3Ryb21fYmlncmFtcyA8LSBuc3Ryb21fdHdlZXRzX2RheSAlPiUKICB1bm5lc3RfdG9rZW5zKGJpZ3JhbSwgc3RyaXBwZWRfdGV4dCwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpCgpiaWdyYW1zX3NlcGFyYXRlZCA8LSBuc3Ryb21fYmlncmFtcyAlPiUKICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikKCiNob3cgb2Z0ZW4gd29yZHMgYXJlIHByZWNlZGVkIGJ5IGEgd29yZCBsaWtlIOKAnG5vdOKAnToKCmJpZ3JhbXNfc2VwYXJhdGVkMiA8LSBiaWdyYW1zX3NlcGFyYXRlZCAlPiUKICBkcGx5cjo6ZmlsdGVyKHdvcmQxID09ICJub3QiKSAlPiUKICBkcGx5cjo6Y291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkKCkFGSU5OIDwtIGdldF9zZW50aW1lbnRzKCJhZmlubiIpCgojV2UgY2FuIHRoZW4gZXhhbWluZSB0aGUgbW9zdCBmcmVxdWVudCB3b3JkcyB0aGF0IHdlcmUgcHJlY2VkZWQgYnkg4oCcbm904oCdIGFuZCB3ZXJlIGFzc29jaWF0ZWQgd2l0aCBhIHNlbnRpbWVudC4Kbm90X3dvcmRzIDwtIGJpZ3JhbXNfc2VwYXJhdGVkMiAlPiUKICBkcGx5cjo6ZmlsdGVyKHdvcmQxID09ICJub3QiKSAlPiUKICBkcGx5cjo6aW5uZXJfam9pbihBRklOTiwgYnkgPSBjKHdvcmQyID0gIndvcmQiKSkgJT4lCiAgZHBseXI6OmNvdW50KHdvcmQyLCBzY29yZSwgc29ydCA9IFRSVUUpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lIAogIGRwbHlyOjpyZW5hbWUoIm4iID0gIm4iKQoKI3doaWNoIHdvcmRzIGNvbnRyaWJ1dGVkIHRoZSBtb3N0IGluIHRoZSDigJx3cm9uZ+KAnSBkaXJlY3Rpb24uIFRvIGNvbXB1dGUgdGhhdCwgd2UgY2FuIG11bHRpcGx5IHRoZWlyIHNjb3JlIGJ5IHRoZSBudW1iZXIgb2YgdGltZXMgdGhleSBhcHBlYXIgKHNvIHRoYXQgYSB3b3JkIHdpdGggYSBzY29yZSBvZiArMyBvY2N1cnJpbmcgMTAgdGltZXMgaGFzIGFzIG11Y2ggaW1wYWN0IGFzIGEgd29yZCB3aXRoIGEgc2VudGltZW50IHNjb3JlIG9mICsxIG9jY3VycmluZyAzMCB0aW1lcykuIAoKcGxvdF9ub3QgPSBub3Rfd29yZHMgJT4lCiAgZHBseXI6Om11dGF0ZShjb250cmlidXRpb24gPSBuICogLXNjb3JlKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGFicyhjb250cmlidXRpb24pKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh3b3JkMiA9IHJlb3JkZXIod29yZDIsIGNvbnRyaWJ1dGlvbikpICU+JQogIGdncGxvdChhZXMod29yZDIsIC1uICogc2NvcmUsIGZpbGwgPSBuICogc2NvcmUgPiAwLCAKICAgICAgICAgICAgIHRleHQgPSBwYXN0ZSgibm90ICIsIHdvcmQyLCAiPGJyPiBTY29yZToiLCAtbiAqIHNjb3JlKSkpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDIiKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHhsYWIoIldvcmRzIHByZWNlZGVkIGJ5IE5PVCIpICsKICB5bGFiKCJTZW50aW1lbnQgc2NvcmUgKiBudW1iZXIgb2Ygb2NjdXJyZW5jZXMgKiAtMSIpICsKICBjb29yZF9mbGlwKCkKCmdncGxvdGx5KHBsb3Rfbm90LCB0b29sdGlwID0gInRleHQiLCB3aWR0aCA9IDkwMCwgaGVpZ2h0ID0gNTUwKSAlPiUgCiAgbGF5b3V0KHNob3dsZWdlbmQgPSBGQUxTRSwgbWFyZ2luID0gbGlzdChiID0gNTAsIGwgPSA1MCkpCgoKIyBub3Rfd29yZHMgJT4lCiMgICBtdXRhdGUoY29udHJpYnV0aW9uID0gKG5uICogc2NvcmUpKSAlPiUKIyAgIGFycmFuZ2UoZGVzYyhhYnMoY29udHJpYnV0aW9uKSkpICU+JQojICAgaGVhZCgyMCkgJT4lCiMgICBtdXRhdGUod29yZDIgPSByZW9yZGVyKHdvcmQyLCBjb250cmlidXRpb24pKSAlPiUKIyAgIGdncGxvdChhZXMod29yZDIsIG5uICogc2NvcmUsIGZpbGwgPSBubiAqIHNjb3JlID4gMCkpICsKIyAgIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKIyAgIHhsYWIoIldvcmRzIHByZWNlZGVkIGJ5IFwibm90XCIiKSArCiMgICB5bGFiKCJTZW50aW1lbnQgc2NvcmUgKiBudW1iZXIgb2Ygb2NjdXJyZW5jZXMgKiAtMSIpICsKIyAgIGNvb3JkX2ZsaXAoKQoKCmBgYAoKTm90ZSB0aGF0IHdlIG11bHRpcGxpZWQgdGhlIHNlbnRpbWVudCBzY29yZSBieSAtMSB0byByZWZsZWN0IHRoZSBpbnZlcnNlIGltcGFjdCBvZiB0aGUgd29yZCAibm90LiIgSGVyZSBpcyB0aGUgc2FtZSBjb25jZXB0IGlsbHVzdHJhdGVkIHdpdGggYSBmZXcgbW9yZSBuZWdhdGluZyB3b3JkczogCgpgYGB7cn0KCm5lZ2F0aW9uX3dvcmRzIDwtIGMoIm5vdCIsICJubyIsICJuZXZlciIsICJkb24ndCIpCgpuZWdhdGVkX3dvcmRzIDwtIGJpZ3JhbXNfc2VwYXJhdGVkICU+JQogIGRwbHlyOjpmaWx0ZXIod29yZDEgJWluJSBuZWdhdGlvbl93b3JkcykgJT4lCiAgZHBseXI6OmlubmVyX2pvaW4oQUZJTk4sIGJ5ID0gYyh3b3JkMiA9ICJ3b3JkIikpICU+JQogIGRwbHlyOjpjb3VudCh3b3JkMSwgd29yZDIsIHNjb3JlLCBzb3J0ID0gVFJVRSkgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKQoKcGxvdF9uZWcgPC0gbmVnYXRlZF93b3JkcyAlPiUKICBkcGx5cjo6bXV0YXRlKGNvbnRyaWJ1dGlvbiA9IG4gKiAtc2NvcmUpICU+JQogIGRwbHlyOjphcnJhbmdlKGFicyhkZXNjKGNvbnRyaWJ1dGlvbikpKSAlPiUKICAjaGVhZCgzMCkgJT4lCiAgZHBseXI6Om11dGF0ZSh3b3JkMiA9IHJlb3JkZXIod29yZDIsIGNvbnRyaWJ1dGlvbikpICU+JQogIGdncGxvdChhZXMod29yZDIsIG4gKiAtc2NvcmUsIGZpbGwgPSBuICogc2NvcmUgPiAwLCAKICAgICAgICAgICAgIHRleHQgPSBwYXN0ZSh3b3JkMSwgIiAiLCB3b3JkMiwgIjxicj4gU2NvcmU6IiwgLW4gKiBzY29yZSkpKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gOSkgKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICB4bGFiKCIiKSArCiAgeWxhYigiU2VudGltZW50IHNjb3JlICogbnVtYmVyIG9mIG9jY3VycmVuY2VzIikgKwogIGZhY2V0X3dyYXAofndvcmQxLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWVfeSIpICsgCiAgY29vcmRfZmxpcCgpCgpnZ3Bsb3RseShwbG90X25lZywgdG9vbHRpcCA9ICJ0ZXh0Iiwgd2lkdGggPSA5MDAsIGhlaWdodCA9IDU1MCkgJT4lIAogIGxheW91dChzaG93bGVnZW5kID0gRkFMU0UsIG1hcmdpbihyID0gMjAsIGIgPSA1MCwgbCA9IDIwKSkKCmBgYAoKQ2VydGFpbiBhZGplY3RpdmVzIGFuZCBhZHZlcmJzIGFyZSB1c2VkIGZvciBlbXBoYXNpcyAtLSBfdmVyeSwgcmVhbGx5LCBleHRyZW1lbHksIG9ubHksIGFjdHVhbGx5LCBzby4uLl8gZXRjLiBXZSBjYW4gZG91YmxlIHRoZSBzY29yZSBvZiB0aGUgc2Vjb25kIHdvcmQgc2NvcmUgdG8gcmVmbGVjdCB0aGUgaW1wYWN0IGZyb20gdGhlIGZpcnN0IHdvcmQuICAKCmBgYHtyfQoKCm5zdHJvbV9jb3VudHMgPSBuc3Ryb21fdHdlZXRzX2RheSAlPiUgCnVubmVzdF90b2tlbnMod29yZCwgc3RyaXBwZWRfdGV4dCkgJT4lIApkcGx5cjo6Z3JvdXBfYnkod29yZCkgJT4lIApkcGx5cjo6c3VtbWFyaXplKG4gPSBsZW5ndGgod29yZCkpICU+JQpkcGx5cjo6YXJyYW5nZShkZXNjKG4pKSAlPiUgCmRwbHlyOjp1bmdyb3VwKCkgCgpuX3kgPSBuc3Ryb21fY291bnRzICU+JSBkcGx5cjo6ZmlsdGVyKHN0cl9kZXRlY3Qod29yZCwgInkkIikpCgplbXBoYXNpc193b3JkcyA8LSBjKCJ2ZXJ5IiwgInJlYWxseSIsICJleHRyZW1lbHkiLCAib25seSIsICJhY3R1YWxseSIsICJzbyIpCgplbXBoYXRpY193b3JkcyA8LSBiaWdyYW1zX3NlcGFyYXRlZCAlPiUKICBkcGx5cjo6ZmlsdGVyKHdvcmQxICVpbiUgZW1waGFzaXNfd29yZHMpICU+JQogIGRwbHlyOjppbm5lcl9qb2luKEFGSU5OLCBieSA9IGMod29yZDIgPSAid29yZCIpKSAlPiUKICBkcGx5cjo6Y291bnQod29yZDEsIHdvcmQyLCBzY29yZSwgc29ydCA9IFRSVUUpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkKCnBsb3QgPC0gZW1waGF0aWNfd29yZHMgJT4lCiAgZHBseXI6Om11dGF0ZShjb250cmlidXRpb24gPSAyKiBuICogc2NvcmUpICU+JQogIGRwbHlyOjphcnJhbmdlKGFicyhkZXNjKGNvbnRyaWJ1dGlvbikpKSAlPiUKICBoZWFkKDUwKSAlPiUKICBkcGx5cjo6bXV0YXRlKHdvcmQyID0gcmVvcmRlcih3b3JkMiwgY29udHJpYnV0aW9uKSkgJT4lCiAgZ2dwbG90KGFlcyh3b3JkMiwgMiAqIG4gKiBzY29yZSwgZmlsbCA9IG4gKiBzY29yZSA+IDAsIAogICAgICAgICAgICB0ZXh0ID0gcGFzdGUod29yZDEsICIgIiwgd29yZDIsICI8YnI+IFNjb3JlOiIsIG4gKiBzY29yZSkpKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIiwgZGlyZWN0aW9uID0gLTEpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDkpICsgCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHhsYWIoIiIpICsKICB5bGFiKCJTZW50aW1lbnQgc2NvcmUgKiBudW1iZXIgb2Ygb2NjdXJyZW5jZXMgKiAyIikgKwogIGZhY2V0X3dyYXAofndvcmQxLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWVfeSIpICsgCiAgY29vcmRfZmxpcCgpIAoKZ2dwbG90bHkocGxvdCwgdG9vbHRpcCA9ICJ0ZXh0Iiwgd2lkdGggPSA5MDAsIGhlaWdodCA9IDU1MCkgJT4lIAogIGxheW91dChzaG93bGVnZW5kID0gRkFMU0UsIG1hcmdpbiA9IGxpc3QociA9IDIwLCBiID0gNTAsIGwgPSA4MCkpCgoKYGBgCiMjTmV0d29yayBBbmFseXNpcwpGaW5hbGx5LCB3ZSBjYW4gdmlzdWFsaXplIGEgbmV0d29yayBvZiBiaWdyYW1zIChwYWlyZWQgd29yZHMpOiAKCmBgYHtyfQoKYmlncmFtc19maWx0ZXJlZCA8LSBiaWdyYW1zX3NlcGFyYXRlZCAlPiUKICBkcGx5cjo6ZmlsdGVyKCF3b3JkMSAlaW4lIHN0b3Bfd29yZHMkd29yZCkgJT4lCiAgZHBseXI6OmZpbHRlcighd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQpCgpiaWdyYW1fY291bnRzIDwtIGJpZ3JhbXNfZmlsdGVyZWQgJT4lIAogIGRwbHlyOjpjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKQoKbGlicmFyeShpZ3JhcGgpCgpiaWdyYW1fZ3JhcGggPC0gYmlncmFtX2NvdW50cyAlPiUKICBkcGx5cjo6ZmlsdGVyKG4gPiA1MCkgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkKCiNiaWdyYW1fZ3JhcGgKCmxpYnJhcnkoZ2dyYXBoKQoKc2V0LnNlZWQoMjAxNykKCgpnZ3JhcGgoYmlncmFtX2dyYXBoLCBsYXlvdXQgPSAiZnIiKSArICAKICB0aGVtZV92b2lkKCkgKyAgIAogIGdlb21fZWRnZV9saW5rKGFlcyhlZGdlX2FscGhhID0gMSwgY29sb3IgPSAicmVkIiksIGFycm93ID0gYXJyb3codHlwZSA9ICJjbG9zZWQiLCBsZW5ndGg9dW5pdCguMDc1LCAiaW5jaGVzIikpLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArICAKICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAidHVycXVvaXNlIiwgc2l6ZSA9IDQsIGFscGhhID0gLjUpICsgIAogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCBzaXplID0gMywgdmp1c3QgPSAxLCBoanVzdCA9IDEpIAoKYGBgCjxwPlRoYW5rcyBmb3IgcmVhZGluZyB0aHJvdWdoOyB0aGlzIHByb2plY3Qgd2FzIGZhc2NpbmF0aW5nIHRvIHdvcmsgb24gYW5kIHdlIGxvb2sgZm9yd2FyZCB0byBhcHBseWluZyB0ZXh0IG1pbmluZyB0byBtb3JlIGFyZWFzIHdpdGhpbiBOb3Jkc3Ryb20uPC9wPgoK